package com.siashan.toolkit.lock4j;

import com.siashan.toolkit.lock4j.executor.LockExecutor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * redisson 重入锁
 *
 * @author siashan
 *
 * @since v1.0.1
 */
@Slf4j
public class RedissonLockExecutor implements LockExecutor<RLock> {

    private RedissonClient redissonClient;


    public RedissonLockExecutor(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * 加锁
     *
     * @param lockKey        锁标识
     * @param lockValue      锁值
     * @param expire         锁有效时间
     * @param acquireTimeout 获取锁超时时间
     * @return 锁信息
     */
    @Override
    public RLock acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        log.debug("redisson acquire lock : {}" ,lockKey);
        try {
            final RLock lockInstance = redissonClient.getLock(lockKey);
            final boolean locked = lockInstance.tryLock(acquireTimeout, expire, TimeUnit.MILLISECONDS);
            log.debug("redisson acquire lock : {}" , locked ? "success" : "fail");
            return locked ? lockInstance : null;
        } catch (InterruptedException e) {
            return null;
        }
    }


    /**
     * 解锁
     *
     * <pre>
     * 为何解锁需要校验lockValue
     * 客户端A加锁，一段时间之后客户端A解锁，在执行releaseLock之前，锁突然过期了。
     * 此时客户端B尝试加锁成功，然后客户端A再执行releaseLock方法，则将客户端B的锁给解除了。
     * </pre>
     *
     * @param key          加锁key
     * @param value        加锁value
     * @param lockInstance 锁实例
     * @return 是否释放成功
     */
    @Override
    public boolean releaseLock(String key, String value, RLock lockInstance) {
        log.debug("redisson releaseLock lock : {}" ,key);
        if (lockInstance.isHeldByCurrentThread()) {
            try {
                return lockInstance.forceUnlockAsync().get();
            } catch (ExecutionException | InterruptedException e) {
                return false;
            }
        }
        return false;
    }

}
